﻿Imports OpenSSL.Crypto
Imports OpenSSL.Crypto.RSA

''' <summary>
'''OpenSllNet
''' </summary>
Public Class OpenSllNet

    Public Sub test()
        Dim strData As String
        Dim clsOpenSSL As New OpenSllNet
        'clsOpenSSL.testOpenSSL()
        strData = System.IO.File.ReadAllText("OpenSslKey\DataByPublicKey2012.data")
        'Dim bteEnData() As Byte = clsOpenSSL.HexStrToByte(strData)
        'clsOpenSSL.Decrypt(strData, "PublicKey2012.pem")
    End Sub

    Public Sub testOpenSSL()
        Dim strData As String = "hello 秦松甫　world!　２０１３"
        Dim bteData() As Byte = System.Text.Encoding.UTF8.GetBytes(strData)
        Dim bteEnData() As Byte
        Dim bteDeData() As Byte
        Dim retData As String

        'Dim rsaTemp As New RSA
        'rsaTemp.GenerateKeys(1024, 65537, Nothing, Nothing)

        'System.IO.File.WriteAllText("OpenSslKey\PublicKey2012.pem", rsaTemp.PublicKeyAsPEM)
        'System.IO.File.WriteAllText("OpenSslKey\PrivateKey2012.pem", rsaTemp.PrivateKeyAsPEM)

        Dim rsaEn = RSA.FromPublicKey(GetInFile("OpenSslKey\PublicKey2013.pem"))
        Dim rsaDe = RSA.FromPrivateKey(GetInFile("OpenSslKey\PrivateKey2013.pem"))

        bteEnData = rsaEn.PublicEncrypt(bteData, Padding.PKCS1)
        'Console.WriteLine("PublicEncrypt:" & vbCrLf & System.Text.Encoding.Unicode.GetString(bteEnData))

        'System.IO.File.WriteAllBytes("OpenSslKey\DataByPublicKey2012.data", bteEnData)
        'Dim bteEnData1() As Byte = System.IO.File.ReadAllBytes("OpenSslKey\DataByPublicKey2012.data")

        System.IO.File.WriteAllText("OpenSslKey\DataByPublicKey2013.data", ByteToHexStr(bteEnData))
        Dim strEnData1 As String = System.IO.File.ReadAllText("OpenSslKey\DataByPublicKey2013.data")
        Dim bteEnData1() As Byte = HexStrToByte(strEnData1)

        bteDeData = rsaDe.PrivateDecrypt(bteEnData, Padding.PKCS1)
        retData = System.Text.Encoding.UTF8.GetString(bteDeData)
        'Console.WriteLine("PrivateEncrypt:" & vbCrLf & retData)

        'Decrypt(strEnData1, "PublicKey2013.pem")
    End Sub

    Public Function Decrypt(ByVal strEnData As String, ByVal strPrivateKeyPath As String, ByVal strPass As String) As String
        Dim retData As String = ""

        Try
            Dim bteEnData() As Byte = HexStrToByte(strEnData)
            Dim privateKeyFile As OpenSSL.Core.BIO = GetInFile(strPrivateKeyPath)
            Dim rsaDe = RSA.FromPrivateKey(privateKeyFile, _
                                           New OpenSSL.Core.PasswordHandler(Function(b As Boolean, o As Object) strPass), _
                                           Nothing)

            'Dim bteDeData = rsaDe.PrivateDecrypt(bteEnData, Padding.PKCS1)
            Dim bteDeData = rsaDe.PrivateDecrypt(bteEnData, Padding.OAEP)
            retData = System.Text.Encoding.UTF8.GetString(bteDeData)

            bteEnData = Nothing
            privateKeyFile.Dispose()
            privateKeyFile = Nothing
            rsaDe.Dispose()
            rsaDe = Nothing
            bteDeData = Nothing

            Return retData
        Catch ex As Exception
            SystemError.SystemLog(ex)
            Return ""
        End Try
    End Function

    ''' <summary> 
    ''' ByteToHexStr 
    ''' </summary> 
    ''' <param name="bytes"></param> 
    ''' <returns></returns> 
    Public Function ByteToHexStr(ByVal bytes As Byte()) As String
        Dim returnStr As String = ""
        If bytes IsNot Nothing Then
            For i As Integer = 0 To bytes.Length - 1
                returnStr += bytes(i).ToString("X2")
            Next
        End If
        Return returnStr
    End Function

    '''<summary> 
    ''' HexStrToByte
    ''' </summary> 
    ''' <param name="hex"></param> 
    ''' <returns></returns> 
    Public Function HexStrToByte(ByVal hex As String) As Byte()
        If hex Is Nothing Then
            Throw New ArgumentNullException("hex")
        End If
        hex = hex.Replace(",", "")
        hex = hex.Replace(vbLf, "")
        hex = hex.Replace("\", "")
        hex = hex.Replace(" ", "")
        If hex.Length Mod 2 <> 0 Then
            '空白の場合
            hex += "20"
        End If
        ' hexStrToByte 
        Dim bytes As Byte() = New Byte(hex.Length \ 2 - 1) {}
        For i As Integer = 0 To bytes.Length - 1
            Try
                ' ２つ数字ごとに、１つbyteにする。 
                bytes(i) = Byte.Parse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber)
            Catch
                ' Rethrow an exception with custom message. 
                Throw New ArgumentException("hex is not a valid hex number!", "hex")
            End Try
        Next
        Return bytes
    End Function

    Public Function GetInFile(ByVal infile As String) As OpenSSL.Core.BIO
        Dim retBio As OpenSSL.Core.BIO

        Try
            If String.IsNullOrEmpty(infile) Then
                retBio = OpenSSL.Core.BIO.MemoryBuffer()
                Dim cin As System.IO.Stream = Console.OpenStandardInput()
                Dim buf As Byte() = New Byte(1023) {}
                While True
                    Dim len As Integer = cin.Read(buf, 0, buf.Length)
                    If len = 0 Then
                        Exit While
                    End If
                    retBio.Write(buf, len)
                End While

                cin.Close()
                cin.Dispose()
                Return retBio
            End If

            Return OpenSSL.Core.BIO.File(infile, "r")
        Catch ex As Exception
            SystemError.SystemLog(ex)
            Return Nothing
        Finally
            retBio = Nothing
        End Try
    End Function

    ''' <summary>
    ''' 文字列を暗号化する
    ''' </summary>
    ''' <param name="sourceString">暗号化する文字列</param>
    ''' <param name="password">暗号化に使用するパスワード</param>
    ''' <returns>暗号化された文字列</returns>
    Public Function EncryptString(ByVal sourceString As String, _
                                         ByVal password As String) As String
        Try
            'RijndaelManagedオブジェクトを作成
            Dim rijndael As New System.Security.Cryptography.RijndaelManaged()

            'パスワードから共有キーと初期化ベクタを作成
            Dim key As Byte() = Nothing
            Dim iv As Byte() = Nothing
            GenerateKeyFromPassword(password, rijndael.KeySize, key, rijndael.BlockSize, iv)
            rijndael.Key = key
            rijndael.IV = iv

            '文字列をバイト型配列に変換する
            Dim strBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(sourceString)

            '対称暗号化オブジェクトの作成
            Dim encryptor As System.Security.Cryptography.ICryptoTransform = _
                rijndael.CreateEncryptor()
            'バイト型配列を暗号化する
            Dim encBytes As Byte() = encryptor.TransformFinalBlock(strBytes, 0, strBytes.Length)
            '閉じる
            encryptor.Dispose()

            'バイト型配列を文字列に変換して返す
            Return System.Convert.ToBase64String(encBytes)

        Catch ex As Exception
            SystemError.SystemLog(ex)
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' 暗号化された文字列を復号化する
    ''' </summary>
    ''' <param name="sourceString">暗号化された文字列</param>
    ''' <param name="password">暗号化に使用したパスワード</param>
    ''' <returns>復号化された文字列</returns>
    Public Function DecryptString(ByVal sourceString As String, _
                                         ByVal password As String) As String
        Try
            'RijndaelManagedオブジェクトを作成
            Dim rijndael As New System.Security.Cryptography.RijndaelManaged()

            'パスワードから共有キーと初期化ベクタを作成
            Dim key As Byte() = Nothing
            Dim iv As Byte() = Nothing
            GenerateKeyFromPassword(password, rijndael.KeySize, key, rijndael.BlockSize, iv)
            rijndael.Key = key
            rijndael.IV = iv

            '文字列をバイト型配列に戻す
            Dim strBytes As Byte() = System.Convert.FromBase64String(sourceString)

            '対称暗号化オブジェクトの作成
            Dim decryptor As System.Security.Cryptography.ICryptoTransform = _
                rijndael.CreateDecryptor()
            'バイト型配列を復号化する
            '復号化に失敗すると例外CryptographicExceptionが発生
            Dim decBytes As Byte() = decryptor.TransformFinalBlock(strBytes, 0, strBytes.Length)
            '閉じる
            decryptor.Dispose()

            'バイト型配列を文字列に戻して返す
            Return System.Text.Encoding.UTF8.GetString(decBytes)

        Catch ex As Exception
            SystemError.SystemLog(ex)
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' パスワードから共有キーと初期化ベクタを生成する
    ''' </summary>
    ''' <param name="password">基になるパスワード</param>
    ''' <param name="keySize">共有キーのサイズ（ビット）</param>
    ''' <param name="key">作成された共有キー</param>
    ''' <param name="blockSize">初期化ベクタのサイズ（ビット）</param>
    ''' <param name="iv">作成された初期化ベクタ</param>
    Private Sub GenerateKeyFromPassword(ByVal password As String, _
                                               ByVal keySize As Integer, _
                                               ByRef key As Byte(), _
                                               ByVal blockSize As Integer, _
                                               ByRef iv As Byte())
        'パスワードから共有キーと初期化ベクタを作成する
        'saltを決める
        Dim salt As Byte() = System.Text.Encoding.UTF8.GetBytes("saltは必ず8バイト以上")
        'Rfc2898DeriveBytesオブジェクトを作成する
        Dim deriveBytes As New System.Security.Cryptography.Rfc2898DeriveBytes( _
            password, salt)
        '.NET Framework 1.1以下の時は、PasswordDeriveBytesを使用する
        'Dim deriveBytes As New System.Security.Cryptography.PasswordDeriveBytes( _
        '    password, salt)

        '反復処理回数を指定する デフォルトで1000回
        deriveBytes.IterationCount = 1000

        '共有キーと初期化ベクタを生成する
        key = deriveBytes.GetBytes(keySize \ 8)
        iv = deriveBytes.GetBytes(blockSize \ 8)
    End Sub
End Class
